package com.game.modular.game.match.service;

import com.game.comment.exection.ServiceException;
import com.game.modular.game.battle.info.base.PvpPlayerInfo;
import com.game.modular.game.battle.info.base.PvpPlayerInfoServiceImpl;
import com.game.modular.game.match.service.base.PvpMatchBaseServiceImpl;
import com.game.modular.game.playerMatch.domain.PvpCity;
import com.game.modular.game.playerAssets.service.PlayerBagServiceImpl;
import com.game.modular.game.playerInfo.domain.PlayerMatch;

import com.game.modular.game.playerMatch.service.*;
import com.game.modular.gameCore.map.domain.CoreCity;
import com.game.modular.gameCore.map.service.impl.CoreCityServiceImpl;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.game.modular.game.playerMatch.domain.PvpMatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import static com.game.modular.game.playerMatch.domain.PvpMatch.*;

/**
 * pvp对局信息(PvpMatch)表服务实现类
 *
 * @author cxy
 * @since 2024-07-16 18:08:51
 */
@Slf4j
@Service
public class PvpMatchServiceImpl extends PvpMatchBaseServiceImpl {
    @Resource
    private PvpCityServiceImpl pvpCityService;
    @Resource
    private PlayerBagServiceImpl playerBagService;
    @Resource
    private PvpCityServiceImpl pvpCityServiceImpl;
    @Resource
    private PvpPlayerInfoServiceImpl playerInfoService;
    @Resource
    private PlayerMatchServiceImpl playerMatchServiceImpl;
    @Resource
    private PvpNpcRanksServiceImpl pvpNpcRanksServiceImpl;
    @Resource
    private PvpMatchEndServiceImpl pvpMatchEndServiceImpl;
    @Resource
    private PvpPlayerInfoServiceImpl pvpPlayerInfoServiceImpl;
    @Resource
    private PvpCityBuildServiceImpl pvpCityBuildServiceImpl;
    @Autowired
    private CoreCityServiceImpl coreCityServiceImpl;

    /**
     * 匹配对局
     */
    @Transactional
    public PvpMatch match(
            Long playerId,
            Integer mapId,
            Integer type,
            Integer startThreshold
    ) {
        Long currentMatchId = playerMatchServiceImpl.currentMatchId(playerId);
        if (currentMatchId != 0) {
            return getById(currentMatchId);
        }
        /// //////////////////////////////////////////////////////////////// 获取match
        PvpMatch pvpMatch = obtainPvpMatch(mapId, type, startThreshold);
        /// //////////////////////////////////////////////////////////////// 加入match
        // 判断当前对局人数是否可用开始
        Integer status = pvpMatch.getStatus();
        Set<Long> playerIds = pvpMatch.getPlayerIds();
        Long pvpMatchId = pvpMatch.getId();

        if (playerIds == null || playerIds.isEmpty()) {
            playerIds = new HashSet<>();
        }
        if (playerIds.contains(playerId)) {
            return pvpMatch;
        }

        if (PvpMatch_TYPE_NORMAL.equals(type) && status.equals(PvpMatch_STATUS_NO_YET)) {// 正常匹配模式
            playerIds.add(playerId);
            if (playerIds.size() < 8) {
                // 更新对局人数
                pvpMatch.addPlayerCount(playerId);
                pvpMatch.addPlayerId(playerId);
                updateById(pvpMatch);
                playerMatchServiceImpl.updateMatch(playerId, pvpMatchId);
            } else if (playerIds.size() == 8) {
                // 更新对局人数
                pvpMatch.addPlayerCount(playerId);
                pvpMatch.addPlayerId(playerId);
                updateById(pvpMatch);
                playerMatchServiceImpl.updateMatch(playerId, pvpMatchId);
                return startPvp(pvpMatch);
            }
        }
        if (PvpMatch_TYPE_QUICK.equals(type) && status.equals(PvpMatch_STATUS_NO_YET)) {// 快速匹配模式
            playerIds.add(playerId);
            if (playerIds.size() < startThreshold) {
                // 更新对局人数
                pvpMatch.addPlayerCount(playerId);
                pvpMatch.addPlayerId(playerId);
                updateById(pvpMatch);
                playerMatchServiceImpl.updateMatch(playerId, pvpMatchId);
            } else {
                pvpMatch.setStartTime(new Date());
                pvpMatch.addPlayerCount(playerId);
                pvpMatch.addPlayerId(playerId);
                updateById(pvpMatch);
                playerMatchServiceImpl.updateMatch(playerId, pvpMatchId);
                return startPvp(pvpMatch);
            }
        }
        return pvpMatch;
    }

    private PvpMatch obtainPvpMatch(Integer mapId, Integer type, Integer startThreshold) {
        PvpMatch insert = list(mapId, type, startThreshold).stream().findFirst().orElse(null);
        if (insert == null) {
            // 创建匹配
            insert = builder()
                    .pvpMapId(mapId)
                    .type(type)
                    .status(0)
                    .startThreshold(startThreshold)
                    .winnerIds(new HashSet<>())
                    .playerCount(new HashSet<>())
                    .playerIds(new HashSet<>())
                    .build();
            save(insert);
        }
        return insert;
    }



    /**
     * 开始对局
     */
    @Transactional
    public PvpMatch startPvp(PvpMatch pvpMatch) {
        // 更新对局
        pvpMatch.setStatus(PvpMatch_STATUS_YET);
        updateById(pvpMatch);
        savaCache(pvpMatch);
        // 城池初始化
        List<PvpCity> initCities = pvpCityService.init(pvpMatch);
        // 获取可分配主城
        List<PvpCity> cities = initCities.stream()
                .filter(item->{
                    CoreCity coreCity = coreCityServiceImpl.getCache(item.getCoreCityId());
                    return coreCity.getIsBirthCity() == 1;
                }).collect(Collectors.toList());
        // ///////////获取玩家
        for (Long uid : pvpMatch.getPlayerIds()) {
            // 初始化用户背包 copper
            playerBagService.obtainBackpackItems(uid, 100000L, 4L);
            playerBagService.obtainBackpackItems(uid, 100000L, 5L);

            // 分配城池
            PvpCity city = cities.remove(new Random().nextInt(cities.size()));
            initCities.remove(city);
            pvpCityServiceImpl.initPvpPlayerCity(uid, city);
            playerInfoService.init(city);
        }
        initCities.forEach(pvpCity -> pvpNpcRanksServiceImpl.init(pvpCity.getMatchId(),pvpCity.getCoreCityId()));
        joinMatchHeart(pvpMatch.getId());
        return pvpMatch;
    }


    /**
     * 取消匹配
     */
    public PvpMatch cancelMatch(Long playerId) {
        PlayerMatch playerMatch = playerMatchServiceImpl.get(playerId);
        if (playerMatch.getCurrentMatchId() == null) {
            throw new ServiceException("当前玩家未参加任何对局");
        }
        PvpMatch pvpMatch = getById(playerMatch.getCurrentMatchId());
        if (!Objects.equals(pvpMatch.getStatus(), PvpMatch_STATUS_NO_YET)) {
            throw new ServiceException("该对局已开始");
        }
        pvpMatch.removePlayerId(playerId);
        pvpMatch.removePlayerCount(playerId);
        updateById(pvpMatch);

        playerMatch.setCurrentMatchId(0L);
        playerMatchServiceImpl.updateById(playerMatch);
        return pvpMatch;
    }


    private static final String SURVIVAL_MATCH_IDS = "survivalMatchIds";
    public final LoadingCache<String, Set<Long>> survivalMatchIds = Caffeine.newBuilder()
            .recordStats()
            .build(key -> Collections.emptySet());

    /**
     * 加入对局心跳
     * @param matchId 对局id
     */
    private void joinMatchHeart(Long matchId){
        Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
        if (result == null){
            result = new HashSet<>();
        }
        result.add(matchId);
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, result);
    }

    /**
     * 退出对局心跳
     * @param matchId 对局id
     */
    public void exitMatchHeart(Long matchId){
        Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
        if (result == null){
            log.info("当前没有对局");
            return;
        }
        result.remove(matchId);
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, result);
    }
    @Value("${match.heartTask.time}")
    private Integer heartTaskTime;
    @Value("${match.productTask.time}")
    private Integer productTaskTime;
    @Value("${match.buildingTask.time}")
    private Integer buildingTaskTime;

    @PostConstruct
    public void matchHeart() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        cacheMissingHeart();
        scheduler.scheduleAtFixedRate(this::matchTask, 0, heartTaskTime, TimeUnit.MILLISECONDS);
        scheduler.scheduleAtFixedRate(this::cityBuildingTask, 0, buildingTaskTime, TimeUnit.MILLISECONDS);
        scheduler.scheduleAtFixedRate(this::productTask, 0, productTaskTime, TimeUnit.MILLISECONDS);
    }

    private void cacheMissingHeart() {
        LambdaQueryWrapper<PvpMatch> pvpMatchLambdaQueryWrapper = new LambdaQueryWrapper<>();
        pvpMatchLambdaQueryWrapper.eq(PvpMatch::getStatus, 1);

        Set<Long> activeHeartbeat =
                list(pvpMatchLambdaQueryWrapper).stream().map(
                        PvpMatch::getId
                ).collect(Collectors.toSet());

        Set<Long> missingHeartbeats;
        missingHeartbeats = activeHeartbeat;
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, missingHeartbeats);
    }

    /**
     * 对局任务
     */
    private void matchTask() {
        try {
            Set<Long> matchIds = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (matchIds!=null){
                matchIds.forEach(matchId -> pvpMatchEndServiceImpl.tryWorldSettlement(matchId));
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }

    /**
     * 内政建造任务
     */
    private void cityBuildingTask() {
        try {
            Set<Long> matchIds = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (matchIds!=null){
                matchIds.forEach(matchId -> pvpCityBuildServiceImpl.tryCityBuildingTask(matchId));
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }

    /**
     * 生产力任务
     */
    private void productTask() {
        try {
            Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (result == null) {
                throw new RuntimeException("当前没有对局");
            }
            for (Long matchId : result) {
                // 获取玩家城池
                Map<String, PvpPlayerInfo> pvpPlayerInfoMap = playerInfoService.mapCache(matchId);
                pvpPlayerInfoMap.forEach((ownerUidStr, pvpPlayerInfo) -> {
                    Long ownerUid = Long.valueOf(ownerUidStr);
                    Set<Long> coreCityIds = pvpPlayerInfo.getCurrentCity();
                    coreCityIds.forEach(cityId -> {
                        PvpCity city = pvpCityServiceImpl.getCache(matchId, cityId);
                        Long copperRate = city.getCopperRate();
                        if (copperRate > 0) {
                            playerBagService.obtainBackpackItems(ownerUid, copperRate, 4L);
                        }
                        Long grainRate = city.getGrainRate();
                        if (grainRate > 0) {
                            playerBagService.obtainBackpackItems(ownerUid, grainRate, 5L);
                        }
                        Long readyArmyRate = city.getReadyArmyRate();
                        if (readyArmyRate > 0) {
                            city.setReadyArmy(city.getReadyArmy() + readyArmyRate);
                            pvpCityServiceImpl.update(city);
                        }
                    });
                });
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }

    /**
     * 重置 玩家参与的对局
     */
    public void resetPvp(Long matchId, Long playerId) {
        PvpPlayerInfo pvpPlayerInfo = pvpPlayerInfoServiceImpl.get(matchId, playerId);
        PvpMatch pvpMatch = select(matchId);
        if (pvpPlayerInfo.getIsRuler()==1){
            pvpMatch.addWinnerIds(playerId);
        }
        pvpMatch.removePlayerId(playerId);
        updateById(pvpMatch);
    }
}